#ifndef VSqlSelect_H
#define VSqlSelect_H

#include "VSqlMetaModel.h"
#include "VSqlDataModel.h"

///////////////////////////////////////////////////////////////////////////////
class VSqlSelectWhere;
class VSqlSelectTable;
class VSqlSelectModel;
class VSqlSelect;
///////////////////////////////////////////////////////////////////////////////

class VSqlSelectField
{
public:
	VSqlSelectField
	(
		const QString &keyNameField,
		const QStringList &keyNameRefFieldList = QStringList()
	);
	
	QString keyNameField() const { return m_keyNameField;}
	QStringList keyNameRefFieldList() const { return m_keyNameRefFieldList;}
	
	VSqlSelectField toRefField(bool &isOk) const;

private:
	VSqlSelectField() {}
	friend VSqlSelectWhere;
	
	QString m_keyNameField;
	QStringList m_keyNameRefFieldList;
};

///////////////////////////////////////////////////////////////////////////////

class VSqlParam
{
public:
	VSqlParam(const QString &name,const QVariant &value = QVariant()) :m_name(name),m_value(value) {}
	
	QString name() const { return m_name;}
	QVariant value() const { return m_value;}
	
private:
	VSqlParam() {}
	friend VSqlSelectWhere;

	QString m_name;
	QVariant m_value;
};

///////////////////////////////////////////////////////////////////////////////

class VSqlSelectWhere
{
public:
	enum EData { EData_unknown,EData_field,EData_param,EData_string };
	
	class VData
	{
	public:
		VData() :m_eData(EData_unknown),m_nTable(-1)	{}

		EData eData()	const { return m_eData;}
		
		void setField(const VSqlSelectField &value) { m_field = value; m_eData = EData_field;}
		VSqlSelectField field() const { return m_field;}

		void setMetaField(const VSqlMeta &v,int nTable) { m_metaField = v; m_nTable = nTable;}
		VSqlMeta metaField() const { return m_metaField;}
		int nTable() const { return m_nTable;}
		
		void setParam(const VSqlParam &value) { m_param = value; m_eData = EData_param;}
		VSqlParam param() const { return m_param;}

		void setString(const QString &value) { m_string = value; m_eData = EData_string;}
		QString string() const { return m_string;}

	private:
		EData m_eData;
		VSqlSelectField m_field;
		VSqlMeta m_metaField;
		int m_nTable;
		VSqlParam m_param;
		QString m_string;
	};
	
public:
	VSqlSelectWhere &operator << (const VSqlSelectField &value);
	VSqlSelectWhere &operator << (const VSqlParam &value);
	VSqlSelectWhere &operator << (const QString &value);

	QList<VData> dataList() const { return m_dataList;}
	
	int dataCount() const { return m_dataList.count();}
	VData data(int index) const { return m_dataList[index];}
	void setData(int index,const VData &v) { m_dataList[index] = v;}
	
	QString sqlWhere(const QString &offs) const;
	
private:
	QList<VData> m_dataList;
};

///////////////////////////////////////////////////////////////////////////////

class VSqlSelectRecord
{
public:
	void setIdRec(const QVariant &v) { m_idRec = v;}
	QVariant idRec() const { return m_idRec;}

	QVariantList record() const { return m_record;}
	
private:
	QVariant m_idRec;
	QVariantList m_record;
	friend VSqlSelectTable;
};

///////////////////////////////////////////////////////////////////////////////

class VSqlSelectRecordList
{
public:
	~VSqlSelectRecordList() { clear();}

	void clear();

	int count() const { return m_recordList.count();}
	void append(VSqlSelectRecord *record);
	VSqlSelectRecord *record(int column) { return m_recordList[column];}
	VSqlSelectRecord *find(const QVariant &idRec) const;

	QString info(const QString &offs = "") const;
	
private: 
	QList<VSqlSelectRecord*> m_recordList;
};

///////////////////////////////////////////////////////////////////////////////

class VSqlSelectTable
{
public:
	class VCounters
	{
	public:
		VCounters() :nTable(0),colIdRec(0),nRef(0) {}
		
	private:
		int nTable;
		int colIdRec;
		int nRef;
		friend VSqlSelectTable;
	};
	
public:
	VSqlSelectTable();
	~VSqlSelectTable();

	VSqlMeta metaTable() const { return m_metaTable;}
	VSqlMeta metaIdRec() const { return m_metaIdRec;}

	void clear();
	void setKeyNameTable(VSqlMetaModel *metaModel,const QString &keyNameTable,VCounters &counters);
	void appendField
	(
		VSqlMetaModel *metaModel,
		const VSqlSelectField &field,
		VCounters &counters,
		VSqlSelectWhere::VData *whereData = NULL
	);

	int colIdRec() const { return m_colIdRec;}
	void setRecord(const QSqlRecord &record);
	VSqlSelectRecordList *recordList() { return &m_recordList;}
	void removeRecords();

	QString sqlTableName() { return m_metaTable.itemPtr()->nameInDb().value().toString();}
	QString sqlIdRecFieldName() { return m_metaIdRec.itemPtr()->nameInDb().value().toString();}
	QString tableAlias() const { return QString("t%1").arg(m_nTable);}

	QString sqlSelect (QString offs = "");
	QString sqlFrom   (QString offs = "");
	QString sqlOrderBy(QString offs = "");
	QString sqlRefJoin(VCounters &counters,QString offs = "");
	QString sqlTblJoin(VCounters &counters,QString offs = "");

	QString info(QString offs = "  ");

private:
	VSqlSelectTable *m_parent;
	VSqlMeta m_metaTable;
	VSqlMeta m_metaIdRec;
	int m_nTable;
	int m_colIdRec;
	VSqlSelectRecordList m_recordList;

	typedef QMap<QString,VSqlMeta> VFieldMap;
	VFieldMap m_fieldMap;

	typedef QMap<QString,VSqlSelectTable*> VRefFieldMap;
	VRefFieldMap m_mRefFieldMap;

	friend VSqlSelectModel;
};

///////////////////////////////////////////////////////////////////////////////

class VSqlSelectModel
{
public:
	VSqlSelectModel(VSqlMetaMap *metaMap);
	~VSqlSelectModel() { clear();}

	QDataWidgetMapper *addMapping(QWidget *widget,const VSqlSelectField &field);
	VSqlDataModel* dataModel(const VSqlSelectField &field);

	VSqlDataModel* rootDataModel() { return &m_dataModel;}
	QList<VSqlDataModel*> dataModelList();

private:
	void clear();
	void setModelFrom(VSqlSelectTable &selectTable);
	void setRecord();

	VSqlMetaMap *m_metaMap;
	VSqlDataModelMem m_dataModel;
	QDataWidgetMapper m_dataWidgetMapper;
	VSqlSelectTable *m_selectTable;

	typedef QMap<QString,VSqlSelectModel*> VRefModelMap;
	VRefModelMap m_mRefModelMap;
	
	friend VSqlSelect;
};

///////////////////////////////////////////////////////////////////////////////

class VSqlSelect
{
public:
	VSqlSelect(VSqlMetaModel *metaModel);
	~VSqlSelect();

	void prepare
	(
		const QString &keyNameTable,
		const QList<VSqlSelectField> &selectFieldList,
		VSqlSelectWhere &sqlWhere = VSqlSelectWhere()
	);

	VSqlSelectModel *selectModel();

	void exec(const QList<VSqlParam> &paramList = QList<VSqlParam>());
	bool next();
	
	void test();
	
private:
	VSqlMetaModel *m_metaModel;
	VSqlQuery m_query;
	VSqlSelectTable m_table;
	VSqlSelectModel *m_selectModel;
	QSqlRecord m_queryRecord;
};

///////////////////////////////////////////////////////////////////////////////

#endif
